﻿using Tesla.Content;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Math;
using Tesla.Scene;
using Tesla.Scene.Extension;
using Tesla.Scene.Shape;

namespace TeslaSamples.Renderer {
    [AppDescription("Render to texture", PlatformCapabilities.Direct3D10 | PlatformCapabilities.XNA4, "RenderTargetImage", "RenderTargetDesc", "RenderTargetSource")]
    public class RenderTargetSample : BasicApp {
        private RenderTarget2D _renderTarget;
        private Camera _targetCamera;
        private Box _offscreenCube;
        private Box _box;

        protected override void LoadContent() {
            Window.Title = "Render Target Sample";

            //Create the box we'll render off screen
            _offscreenCube = new Box("Offscreen box", 10, 10, 10);
            _offscreenCube.Material = ContentManager.Load<Material>("LitBasicTexture.tem");
            _offscreenCube.Material.SetParameter("DiffuseMap", ContentManager.Load<Texture2D>("Textures//rock_diff.dds"));
            _offscreenCube.AddController(new RotateController(Vector3.Normalize(Vector3.Add(Vector3.Up, Vector3.Right)), 45.0f));

            PointLight pl = new PointLight();
            pl.Diffuse = Color.CornflowerBlue;
            pl.Position = new Vector3(0, 25, 25);
            pl.Ambient = Color.LightBlue;
            _offscreenCube.AddLight(pl);
            _offscreenCube.SceneHints.LightCombineHint = LightCombineHint.Local;
            _offscreenCube.RenderBucketType = RenderBucketType.Skip;

            //Create a render target that also has a depth stencil.
            _renderTarget = new RenderTarget2D(512, 512, true, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);

            //Camera we'll use for the off screen rendering
            _targetCamera = new Camera(new Viewport(0, 0, 512, 512));
            _targetCamera.Position = new Vector3(0, 0, 50);
            _targetCamera.SetProjection(45.0f, 0.1f, 5000.0f);
            _targetCamera.Update();

            //Create a box that we'll attach to the scene graph and render as normal.
            _box = new Box("OnScreen box", 50, 50, 50);
            _box.Material = ContentManager.Load<Material>("BasicTexture.tem");
            //Since render target is a texture, we can set it to the material's effect.
            _box.Material.SetParameter("DiffuseMap", _renderTarget);
            RootNode.AddChild(_box);
        }

        protected override void OnUpdate(GameTime time) {
            //Since the cube we'll render to a texture isn't attached to the scene graph, ensure we update it here
            _offscreenCube.Update(time);
        }

        protected override void OnRenderPost(IRenderer renderer) {
            //Save the old camera and set the one we'll use to render off screen
            ICamera oldcam = renderer.CurrentCamera;
            renderer.CurrentCamera = _targetCamera;

            //Set the render target
            renderer.SetRenderTarget(_renderTarget);

            //Render directly (calling Draw/OnDraw would be valid here, since we set the render bucket type to skip and
            //if we wanted to check if the box was in view).
            _offscreenCube.Render(renderer);
            
            //Set the current target to null, this tells the renderer to set the backbuffer as the current target,
            //which is the last swapchain used.
            renderer.SetRenderTarget(null);

            //Set the old camera back to the renderer
            renderer.CurrentCamera = oldcam;

            //Due to the setup in BasicApp, the render queue is sorted/rendered after OnRender() is called, which
            //is when the onscreen box will be rendered, so our render target will be ready and waiting
            //to be used as a texture for the shader.
        }
    }
}
